import { getCurrentInstance, onBeforeUpdate, reactive } from "@vue/runtime-core";
import { VNodeChild } from "src/shims";

type SlotFunction = ((vnode?: VNodeChild) => VNodeChild) & { isExit: () => boolean };

type SlotData<T extends string> = {
  slots: {
    default: SlotFunction;
  } & {
    [k in T]: SlotFunction;
  };
};

export function useSlots<T extends string>(names?: T[]): SlotData<T> {
  const ctx = getCurrentInstance()!;
  const slotNames = [...(names || []), "default"];

  const ctxSlots = reactive(
    slotNames.reduce(
      (prev: any, slotName) => ((prev[slotName] = ctx.slots[slotName] || null), prev),
      {}
    )
  );

  /*
   * ctx.slot 不是响应式属性，compuetd和watch都无法触发更新
   * 利用 onBeforeUpdate 在render 函数触发前先更新 ctxSlots 响应式变量
   */
  onBeforeUpdate(() =>
    slotNames.forEach(slotName => (ctxSlots[slotName] = ctx.slots[slotName] || null))
  );

  let slots = slotNames.reduce((prev: any, slotName: string) => {
    prev[slotName] = Object.assign(
      (vnode: VNodeChild) => {
        const slot = ctxSlots[slotName];
        return slot ? slot() : vnode;
      },
      {
        isExit() {
          return !!ctxSlots[slotName];
        }
      }
    );
    return prev;
  }, {} as any);

  return {
    slots
  };
}
